<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */

namespace app\commands;

use app\components\Common;
use app\models\Task;
use app\models\TaskItem;
use Yii;
use Exception;
use yii\console\Controller;

/**
 * 处理器
 */
class HandleController extends Controller
{
    const MAX_NUM = 1000;      //最大进程数

    /**
     * 循环生成
     */
    public function actionLoopTask(){
        while(1){
            try{
                $this->actionTask();
            }catch(Exception $e){
                Common::printLog($e->getMessage());
            }
            sleep(5);
        }
    }

    /**
     * 父进程分配任务给worker
     */
    public function actionTask(){
        $tasks = $this->__getTasks();
        Common::printLog("本次需要分配任务... 共:".count($tasks) );
        Yii::$app->db->close();
        foreach ($tasks as $task) {
            $pid = pcntl_fork();
            switch ($pid) {
                case -1:
                    Common::printLog("fork error" . $pid);
                    exit;
                case 0:
                    $this->worker($task);
                    exit(0);
                default:
                    Common::printLog("开始子进程:" . $pid);
            }
        }

        $status = null;
        while (pcntl_waitpid(0, $status, WUNTRACED) != -1) {
            pcntl_wexitstatus($status);
            Common::printLog("子进程结束.");
        }

        Common::printLog("完成一次多进程任务");
    }

    private function __getTasks(){
        $sql ="SELECT
                *,
                t.id as 'tid',
                ti.id as 'iid'
                FROM url_task_item ti
                 LEFT JOIN url_task t
                 ON ti.`task_id` = t.id
                WHERE ti.status = 0
                AND ti.`visit_cnt` < 10
                limit ".self::MAX_NUM;
        $result = Yii::$app->db->createCommand($sql)->queryAll();

        if(empty($result)){
            throw new Exception("没有要执行的任务",200);
        }
        //取出则表示处理中
        $task_id = [];
        foreach ($result as $item) {
            $task_id[] = $item['iid'];
        }
        $update_sql = "UPDATE url_task_item SET STATUS= ".TaskItem::STATUS_PROCESSING.",visit_cnt = visit_cnt+1 WHERE id IN (".implode(',',$task_id).")";
        Yii::$app->db->createCommand($update_sql)->execute();

        return $result;
    }

    /**
     * 子进程
     */
    private function worker($task) {
        try {
            $transaction = Yii::$app->db->beginTransaction();
            $url = $task['url']."?".$task['arg'];
            $response = Common::request($url,$task['method'],$task['host'],$task['lang'],$task['user_agent']);

            if(empty($response)){
                throw new Exception("返回值为空",500 );
            }

            $type = TaskItem::TYPE_NORMAL;
            $proccess_type = explode(",",$task['analyze_type']);
            if(strlen($response) > 4){
                if(in_array(TaskItem::TYPE_LIANTONG,$proccess_type)){
                    $match  = preg_match("#[A-Z]{32,}#",$response);

                    if($match){
                        $type = TaskItem::TYPE_LIANTONG;
                        goto end;
                    }
                }

                if(in_array(TaskItem::TYPE_JS,$proccess_type)){
                    if(substr($task['url'],-3) == '.js' && (strpos('download',$response) || strpos('.exe',$response))){
                        $type = TaskItem::TYPE_JS;
                        goto end;
                    }
                }

                if(in_array(TaskItem::TYPE_PE,$proccess_type)){
                    if(substr($task['url'],2) == 'MZ'){
                        $type = TaskItem::TYPE_PE;
                        goto end;
                    }
                }

                if(in_array(TaskItem::TYPE_ELF,$proccess_type)){
                    if(substr($task['url'],4) == '\x7fELF'){
                        $type = TaskItem::TYPE_ELF;
                        goto end;
                    }
                }

            }
            end:

            //写入结果
            $update_item_sql = "UPDATE url_task_item SET type = ".$type.",status= ".TaskItem::STATUS_DONE." WHERE id =".$task['iid'];
            Yii::$app->db->createCommand($update_item_sql)->execute();

            $update_task_sql = "UPDATE url_task SET analyze_cnt=analyze_cnt+ 1 WHERE id =".$task['tid'];
            Yii::$app->db->createCommand($update_task_sql)->execute();

            Common::printLog('完成一条检测'.$task['iid']);

            $transaction->commit();
        } catch (Exception $e) {
            $transaction->rollback();
            $msg = $e->getMessage();
            //写入结果
            $update_item_sql = "UPDATE url_task_item SET msg = '".$msg."',status= ".TaskItem::STATUS_ERROR." WHERE id =".$task['iid'];
            Yii::$app->db->createCommand($update_item_sql)->execute();

            $update_task_sql = "UPDATE url_task SET analyze_cnt=analyze_cnt+ 1 WHERE id =".$task['tid'];
            Yii::$app->db->createCommand($update_task_sql)->execute();

            Common::printLog("出错，详细信息：".$msg);
        }
    }
}
